package testApplets;

import java.awt.Container;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;

import marchingCubes.CAADMCube;

import blobDetector.Blob;
import blobDetector.BlobDetector;

import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PGraphics;
import processing.core.PImage;
import simpleGeom.Box3D;
import simpleGeom.DepthAnalyser2D;
import simpleGeom.DepthAnalyser3D;
import simpleGeom.FloatGrid3D;
import simpleGeom.IntGrid2D;
import simpleGeom.Line3D;
import simpleGeom.Rect2D;
import simpleGui.Gui;
import simpleGui.GuiClickButton;
import simpleGui.GuiSpinnerGroup;
import simpleGui.PCam2DGraphics;
import testApplets.StreamMeshDistance.Boundary;

public class Copy_2_of_FinalCutBrowser extends PApplet {
	Gui gui;
	GuiSpinnerGroup spinnerFrom;
	GuiSpinnerGroup spinnerTo;
	PImage imgCanvas;
	PImage imgBlobs;
	PImage imgNew;
	PImage imgOffset;
	boolean[]solids;
	boolean[]repairGrid;
	int nX=1600;
	int nY;
	int nZ=1620;
	String pathCuts="/Users/caad/Desktop/Grotto - First Half V/data/cuts";
	String pathFixed="/Users/caad/Desktop/Grotto - First Half V/data/fixed";
	String pathBoxes="/Users/caad/Desktop/Grotto - First Half V/data/boxes.txt";
	String pathOffset="/Users/caad/Desktop/Grotto - First Half V/data/offset";
	float scale;
	PCam2DGraphics cam;
	final static int SOLID=0;
	final static int PARTS=1;
	int mode=SOLID;
	
	
	int bdX1= 50+1;
	int bdX2= 1549-1;
	int bdY1= 55;
	int bdY2= 3269;
	int bdZ1= 49+1;
	int bdZ2= 1589-1;
	Box3D bdsFloat;//this stays for ever

	Boundary[][] boundaries;
	
	
	GuiSpinnerGroup spX1;
	GuiSpinnerGroup spX2;
	GuiSpinnerGroup spY1;
	GuiSpinnerGroup spY2;
	GuiSpinnerGroup spZ1;
	GuiSpinnerGroup spZ2;
	GuiSpinnerGroup selectSpinner;
	GuiClickButton guiAddBox;
	
	int cY=0;


	Box3D currentBox;
	Box3D selectedBox;
	ArrayList<Box3D> fillBoxes=new ArrayList<Box3D>(); 

	PImage img;


	boolean flagSaveBoxes=false;
	boolean flagAddBox=false;
	boolean flagDeleteBox=false;
	int[] coords;
	Box3D offsetBox;
	
	int[] blobs;
	int[] candidates;
	int[] newCandidates;
	ArrayList<Box3D>blobBoxes=new ArrayList<Box3D>();
	ArrayList<Box3D>offsetBoxes=new ArrayList<Box3D>();
	ArrayList<Box3D>offsetBoxes2=new ArrayList<Box3D>();
	ArrayList<Box3D>solidBoxes= new ArrayList<Box3D>();// just make it solid
	ArrayList<Box3D>solidBlobBoxes= new ArrayList<Box3D>();
	ArrayList<Box3D>fillAndSolidBoxes= new ArrayList<Box3D>();
	ArrayList<Box3D>solidBlobBoxes3= new ArrayList<Box3D>();
	ArrayList<Box3D>blobBoxes2=new ArrayList<Box3D>();
	ArrayList<Box3D>blobBoxes3=new ArrayList<Box3D>();
	ArrayList<Box3D>allBoxes=new ArrayList<Box3D>();
	ArrayList<Box3D>subBoxes=new ArrayList<Box3D>();
	ArrayList<Box3D>subSolidBoxes=new ArrayList<Box3D>();

	ArrayList<Line3D>splitLines=new ArrayList<Line3D>();
	
	boolean displayBoxes;
	boolean displayGrid=false;
	int[] yCuts=new int[]{56,456,858,1260,1661,2062,2464,2865,3267};
	int[] boxX;
	int[] boxY;
	int[] boxZ;
	int cyan=color(0,255,0);
	int[][] blobInfo;
	float sX;
	float sY;
	float sZ;
	float eX;
	float eY;
	float eZ;
	String path = "/Users/caad/Desktop/Grotto - First Half V/03_scaled/";
	String voxelPath="/Users/caad/Desktop/Grotto - First Half V/voxel_distance_1mm/";
	VoxelStream vStream;
	PixelBoundary pixBoundsLevel1;
	PixelBoundary pixBoundsLevel2;
	
	float dim=0.001f;
	float iso = dim *0.9f;
	float isoIn=0;
	float isoOut=iso*2f;
	//Box3D cB=new Box3D(0.00f,0.000f,-1.52f,1.5f,3.25f,0.00f);//this stays for ever
	Box3D gridBounds;
	float[] xSegs;
	float[] zSegs;
	int step=1;
	float mirrorZ=0.0135f;
	float moveX=-0.0005f;
	float moveY=0.0055f;
	 PFont myFont;
	 PGraphics textGraphics;
	int iLayer;
	float[][] buffer=new float[nX][nZ];
	float[][] buffer2=new float[nX][nZ];
	float[][] buffer3=new float[nX][nZ];
	PolyBoundary test;
	
	PixelBoundary bdCopyLevel1;
	PixelBoundary bdCopyLevel2;
	PixelBoundary bdCopy;

	//Piamge
	// copy
	PixelBoundary cBoundary;
	PixelBoundary maskFrom;
	PixelBoundary maskFrom2;
	PImage imgMask;
	PImage imgCopy;

	public void setup() {
		iLayer=2;
		step=1;
		
		size(1400, 800);
		initOffsetImages();
		initTextGraphics();
		setupMenu();
		setupGUI();
		setupBounds();
		buffer=new float[nX][nZ];
		buffer2=new float[nX][nZ];
		vStream=new VoxelStream(voxelPath, dim, sX, sY, sZ, nX, nY, nZ);
		solids=new boolean[nX*nZ];
		repairGrid=new boolean[nX*nZ];
		blobs=new int[solids.length];
		candidates=new int[solids.length];
		newCandidates=new int[solids.length];
		imgCanvas=this.createImage(nX, nZ, ARGB);
		imgNew=this.createImage(nX, nZ, ARGB);
		imgBlobs=this.createImage(nX, nZ, ARGB);
		imgMask=this.createImage(nX, nZ, ARGB);
		imgCopy=this.createImage(nX, nZ, ARGB);
		setupCorrections();
		//rebuildImages(1900,2100);
		//makeSolid(1900,2100);
		
		
		for (int i=7;i<8;i++){
			iLayer=i;
			//copyParts(850);
			this.saveObjLevelFast(yCuts[iLayer], yCuts[iLayer+1], boundaries[i],path+"layer"+iLayer);
			//this.saveObjLevelFast(yCuts[iLayer], yCuts[iLayer+1], boundaries[i],path+"v1_"+iLayer);
		}
		//this.saveObjLevel(yCuts[4], yCuts[5], boundaries);
		loadCuts(56,solids);
		writeToImage(solids,imgCanvas);
	}
	public void setupCorrections(){
		//solids
				pixBoundsLevel1=new PixelBoundary(nX,nZ);
				pixBoundsLevel1.fillAll(Boundary.OUTSIDE);
				for (int x = 0; x < nX; x++) {
					for (int z = 560; z < nZ; z++) {
						pixBoundsLevel1.set(x, z, Boundary.INSIDE);
					}
				}
				for (int x = 310; x < nX; x++) {
					for (int z = 0; z < nZ; z++) {
						pixBoundsLevel1.set(x, z, Boundary.INSIDE);
					}
				}
				
				
				pixBoundsLevel2=new PixelBoundary(nX,nZ);
				pixBoundsLevel2.fillAll(Boundary.OUTSIDE);
				pixBoundsLevel2.fillGrid(290, 450, 600, 690, Boundary.INSIDE);
				
				/*for (int x = 350; x < nX; x++) {
					for (int z = 0; z < nZ; z++) {
						pixBoundsLevel2.set(x, z, Boundary.INSIDE);
					}
				}*/
				
				offsetBoxes.add(new Box3D(56,bdY1+2,bdZ1,1000,1660,500));
				offsetBox=offsetBoxes.get(0);
				//println("bdx1: "+bdX1);
				offsetBoxes2.add(new Box3D(bdX1,2609,bdZ1,406,2955,460));
				//offsetBox=;
				blobBoxes.add(new Box3D(56,800,bdZ1,1000,1700,500));
				blobBoxes.add(new Box3D(56,500,bdZ1,300,801,500));
			
				//blobBoxes.add(new Box3D(60,bdY1+2,bdZ1,1000,1660,500)); 
				blobBoxes.add(new Box3D(1200,bdY1+2,630,1550,2400,1580));// hinten links
				blobBoxes.add(new Box3D(740,bdY1+2,170,1390,2550,830));// mitte
				blobBoxes.add(new Box3D(56,242,170,600,677,530));// vorne oben
				blobBoxes.add(new Box3D(bdX1+1,400,070,485,679,540));// vorne oben2
				
				blobBoxes.add(new Box3D(200,1199,056,400,1669,420));// vorne oben2
				blobBoxes.add(new Box3D(292,2516,130,667,2836,551));//  oben
				blobBoxes.add(new Box3D(120,679,160,371,1200,428));//  oben
				blobBoxes.add(new Box3D(bdX1+1,2566,56,265,3000,410));//  oben
				blobBoxes.add(new Box3D(160,1923,bdZ1-1,399,2199,240));//  oben
				blobBoxes.add(new Box3D(400,2246,bdZ1-1,551,2471,250));//  oben
				blobBoxes.add(new Box3D(80,1300,56,255,1655,190));//  oben
				blobBoxes.add(new Box3D(200,2744,66,500,2945,420));//  oben
				blobBoxes.add(new Box3D(200,2756,bdZ1+1,360,2957,220));//  oben
				blobBoxes.add(new Box3D(bdX1+1,2675,280,240,2711,490));//  oben
				blobBoxes.add(new Box3D(756,2600,800,1500,3268,bdZ2-2));//  oben
				
				blobBoxes.add(new Box3D(366,1460,156,1000,2355,680));//  oben
				
				subSolidBoxes.add(new Box3D(boxX[4],780,boxZ[4],1162,1100,1200));
				subSolidBoxes.add(new Box3D(295,1040,522,302,1050,528));
				
				
				float boxD=32;
				subBoxes.add(new Box3D(220,400,390,220+boxD,1715,390+boxD));
				subBoxes.add(new Box3D(80,yCuts[1],80,80+boxD,yCuts[2]+100,80+boxD));
				subBoxes.add(new Box3D(320,950,610,320+boxD,2350,610+boxD));
				
				//corrections layer3 part1 
				float bD=(100-boxD)*0.5f;
				subSolidBoxes.add(new Box3D(220-bD,1000,390-bD,220+boxD+bD,1000+20,390+boxD+bD));
				subSolidBoxes.add(new Box3D(220-bD,1000,390,220+boxD+bD,yCuts[3],390+boxD));
				int mid=this.xK((xSegs[0]+xSegs[1])*0.5f);
				subSolidBoxes.add(new Box3D(mid-10,yCuts[2]+150,63,mid+10,yCuts[3],83));
				
				// connection 3 and 4
				subSolidBoxes.add(new Box3D(200,1310,70,200+boxD,1780,70+boxD));
				
				fillAndSolidBoxes.add(new Box3D((boxX[3]+boxX[4])*0.5f,700,boxZ[2],1550,yCuts[4],1072));
				fillAndSolidBoxes.add(new Box3D((boxX[3]+boxX[4])*0.5f,700,boxZ[2],1550,yCuts[4],1072));

				fillAndSolidBoxes.add(new Box3D(boxX[5],yCuts[0],boxZ[0],boxX[6],yCuts[6]+2,boxZ[4]));
			
				//solidBoxes.add();
				solidBoxes.add(new Box3D(boxX[4],2500,boxZ[2],1550,bdY2,boxZ[4]));
				
				
				
				//level1
				solidBoxes.add(new Box3D(boxX[4],2500,boxZ[2],1550,bdY2,boxZ[4]));
				
				
				solidBlobBoxes.add(new Box3D(180,750,275,230,800,280));
				solidBlobBoxes.add(new Box3D(295,1190,280,297,1200,320));
				fillAndSolidBoxes.add(new Box3D(1220,459,1556,1400,463,1562));
				fillAndSolidBoxes.add(new Box3D(1220,459,1569,1400,463,1574));
				
				//level4
				solidBlobBoxes3.add(new Box3D(680,1900,490,685,2100,780));
				blobBoxes3.add(new Box3D(500,1900,500,700,2100,900));
				solidBlobBoxes3.add(new Box3D(795,1900,880,1100,2100,885));
				blobBoxes3.add(new Box3D(750,1900,875,1100,2100,1100));

				subBoxes.add(new Box3D(280,1925,80,280+boxD,2200,80+boxD));
				subSolidBoxes.add(new Box3D(355,1740,boxX[0],400,1745,100));
				//solidBlobBoxes.add(new Box3D(980,800,boxZ[2],bdX2,2550,bdZ2));// nur linker streifen
				
				solidBlobBoxes.add(new Box3D(980,800,boxZ[4],bdX2,2550,bdZ2));// 
				
				fillAndSolidBoxes.add(new Box3D(1284,1838,1040,bdX2,1900,1320));
				blobBoxes2.add(new Box3D(800,800,450,bdX2,2550,bdZ2));
				blobBoxes2.add(new Box3D(150,750,250,270,800,350));// vorne oben2
				blobBoxes2.add(new Box3D(200,1190,180,310,1200,350));
				
				solidBlobBoxes3.add(new Box3D(bdX1,820,bdZ1,bdX2,1980,boxZ[2]));
				blobBoxes3.add(new Box3D(1170,820,925,1500,1980,1328));
				blobBoxes3.add(new Box3D(1200,bdY1+2,630,1550,2400,1580));
				
				/*maskCopy=new Mask(boxX[0],boxZ[0],500,532);
				maskCopy.h1=678;
				maskCopy.h2=1979;
				maskCopy.fillGrid(240,430, maskCopy.x2, maskCopy.y2, false);
				maskCopy.fillGrid(418,maskCopy.y1, maskCopy.x2, 120, false);// here is a problem*/
				bdCopy=new PixelBoundary(nX,nZ);
				bdCopy.h1=678;
				bdCopy.h2=1979;
				bdCopy.fillAll(Boundary.OUTSIDE);
				bdCopy.fillGrid(boxX[0],boxZ[0],500,532, Boundary.INSIDE);
				bdCopy.fillGrid(240,430, 500,532, Boundary.OUTSIDE);
				bdCopy.fillGrid(418,boxZ[0],500, 120, Boundary.OUTSIDE);
				
				/*maskCopyLevel1=new Mask(boxX[0],boxZ[0],500,532);
				maskCopyLevel1.h1=678;
				maskCopyLevel1.h2=1979;
				maskCopyLevel1.fillGrid(240,430, maskCopyLevel1.x2, maskCopyLevel1.y2, false);
				maskCopyLevel1.fillGrid(325,maskCopyLevel1.y1, maskCopyLevel1.x2, maskCopyLevel1.y2, false);*/
				
				bdCopyLevel1=new PixelBoundary(nX,nZ);
				bdCopyLevel1.h1=678;
				bdCopyLevel1.h2=1979;
				bdCopyLevel1.fillAll(Boundary.OUTSIDE);
				bdCopyLevel1.fillGrid(boxX[0],boxZ[0],500,532, Boundary.INSIDE);
				bdCopyLevel1.fillGrid(240,430, 500,532, Boundary.OUTSIDE);
				bdCopyLevel1.fillGrid(325,boxZ[0],500, 532, Boundary.OUTSIDE);
				
				
				/*maskCopyLevel2=new Mask(boxX[0],boxZ[0],500,532);
				maskCopyLevel2.h1=678;
				maskCopyLevel2.h2=1979;
				maskCopyLevel2.fillGrid(240,430, maskCopyLevel1.x2, maskCopyLevel1.y2, false);
				maskCopyLevel2.fillGrid(420,maskCopyLevel1.y1, maskCopyLevel1.x2, maskCopyLevel1.y2, false);*/
			
				bdCopyLevel2=new PixelBoundary(nX,nZ);
				bdCopyLevel2.h1=678;
				bdCopyLevel2.h2=1979;
				bdCopyLevel2.fillAll(Boundary.OUTSIDE);
				bdCopyLevel2.fillGrid(boxX[0],boxZ[0],500,532, Boundary.INSIDE);
				bdCopyLevel2.fillGrid(240,430, 500,532, Boundary.OUTSIDE);
				bdCopyLevel2.fillGrid(420,boxZ[0],500, 532, Boundary.OUTSIDE);
				
				maskFrom=new PixelBoundary(nX,nZ);
				maskFrom.fillAll(Boundary.OUTSIDE);
				maskFrom.fillGrid(400,boxZ[0],1000,680, Boundary.INSIDE);
				maskFrom.fillGrid(400,boxZ[0],480,500, Boundary.OUTSIDE);
				maskFrom.fillGrid(730,420,1000,680, Boundary.OUTSIDE);
				
				
				maskFrom2=new PixelBoundary(nX,nZ);
				maskFrom2.fillAll(Boundary.OUTSIDE);
				maskFrom2.fillGrid(680,200,900,310, Boundary.INSIDE);
				
				
				allBoxes.addAll(offsetBoxes);
				allBoxes.addAll(offsetBoxes2);
				allBoxes.addAll(solidBoxes);
				allBoxes.addAll(blobBoxes);
				allBoxes.addAll(blobBoxes2);
				allBoxes.addAll(blobBoxes3);
				allBoxes.addAll(solidBlobBoxes);
				allBoxes.addAll(solidBlobBoxes3);
				allBoxes.addAll(fillAndSolidBoxes);
				allBoxes.addAll(subBoxes);
				allBoxes.addAll(subSolidBoxes);
				
				splitLines.add(new Line3D(180,855,585,216,900,585));
				splitLines.add(new Line3D(216,855,585,216,900,572));
				splitLines.add(new Line3D(216,855,572,250,900,572));
				splitLines.add(new Line3D(1260,2062,1495,1275,2080,1495));
	}
	public void setupBounds(){
		Box3D cB=new Box3D(0.00f,0.000f,-1.52f,1.5f,3.25f,0.00f);//this stays for ever
		Box3D gridBounds=cB.getOffsetBox(0.05f);
		int  y1=getRoundDown(gridBounds.y1,dim);
		int  x1=getRoundDown(gridBounds.x1,dim);
		int  z1=getRoundDown(gridBounds.z1,dim);
		
		int  y2=getRoundUp(gridBounds.y2,dim);
		int  x2=getRoundUp(gridBounds.x2,dim);
		int  z2=getRoundUp(gridBounds.z2,dim);
		
		nX=x2-x1;
		nY=y2-y1;
		nZ=z2-z1;
		
		sX=x1*dim;
		sY=y1*dim;
		sZ=z1*dim;
		eX=x2*dim;
		eY=y2*dim;
		eZ=z2*dim;
		
		float bdy1=cB.y1+0.005f;
		
		
		float bdx1=cB.x1;
		float bdz1=cB.z1+0.001f;
		float bdy2=cB.y2-0.030f;
		float bdx2=cB.x2;
		float bdz2=cB.z2+0.020f-0.006f;
		this.bdsFloat=new Box3D(bdx1,bdy1,bdz1,bdx2,bdy2,bdz2);
		
		this.bdX1=xK(bdx1);
		this.bdX2=xK(bdx2);
		this.bdY1=yK(bdy1);
		this.bdY2=yK(bdy2);
		this.bdZ1=zK(bdz1);
		this.bdZ2=zK(bdz2);
		
		float dX=(bdx2-bdx1)/6f;
		xSegs=new float[7];
		String segString="";
		for (int i=0;i<xSegs.length;i++){
			xSegs[i]=bdx1+i*dX;
			segString+=((xSegs[i]-moveX)*1000)+",";
		}
		println("segStringX: "+segString);
		zSegs=new float[7];
		float dZ=(bdz2-bdz1)/6f;
		segString="";
		for (int i=0;i<zSegs.length;i++){
			zSegs[i]=bdz1+i*dZ;
			segString+=((zSegs[i]-mirrorZ)*1000)+",";
		}
		println("segStringZ: "+segString);
		
		int z0=1569;
		boxY=new int[9];
		for (int i=0;i<8;i++){
			boxY[i]=yK((i*(bdy2-bdy1)/8f)+bdy1);
		}
		boxY[8]=yK(bdy2);
		
		
		boxZ=new int[7];
		for (int i=0;i<boxZ.length;i++){
			boxZ[i]=zK(zSegs[i]);
		}
		boxZ[boxZ.length-1]=bdZ2;
		
		boxX=new int[7];
		for (int i=0;i<boxX.length;i++){
			boxX[i]=xK(xSegs[i]);
		}
		boxX[boxX.length-1]=bdX2;
		
		boundaries=new Boundary[8][4];
		for (int i=0;i<6;i++){
			boundaries[i][0]=new Bounda(boxX[5],boxZ[4],boxX[6],boxZ[6]);
			boundaries[i][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
			boundaries[i][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
			boundaries[i][3]=new Bounda(boxX[0],boxZ[1],boxX[5],boxZ[6]);
	
		}
		
		boundaries[0]=new Boundary[5];
		boundaries[0][0]=new Bounda(boxX[5],boxZ[4],boxX[6],boxZ[6]);
		boundaries[0][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[0][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[0][3]=new Bounda(boxX[3],boxZ[1],boxX[5],boxZ[6]);
		boundaries[0][4]=new Bounda(boxX[0],boxZ[1],boxX[3],boxZ[6]);
		
		boundaries[1]=new Boundary[4];
		boundaries[1][0]=new Bounda(boxX[4],boxZ[4],boxX[6],boxZ[6]);
		boundaries[1][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[1][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[1][3]=new Bounda(boxX[0],boxZ[1],boxX[5],boxZ[6]);
		
		
		
		
		PixelBoundary bd4=new PixelBoundary(nX,nZ);
		bd4.fillAll(Boundary.OUTSIDE);
		bd4.fillGrid(boxX[0],boxZ[1],boxX[5],boxZ[6],Boundary.INSIDE);
		bd4.fillGrid(boxX[4],boxZ[4],boxX[6],boxZ[6],Boundary.OUTSIDE);
		bd4.doBorders();
		
		boundaries[1][3]=bd4;
		//Complex
		boundaries[2]=new Boundary[5];
		boundaries[2][0]=new Bounda(boxX[4],boxZ[4],boxX[6],boxZ[6]);
		boundaries[2][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[2][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[2][3]=bd4;
		boundaries[2][4]=new Bounda(boxX[0],boxZ[0],boxX[3],boxZ[3]);
		
		
		PixelBoundary bd3_0=new PixelBoundary(nX,nZ);
		bd3_0.fillAll(Boundary.OUTSIDE);
		bd3_0.fillGrid(boxX[3],boxZ[4],boxX[6],boxZ[6],Boundary.INSIDE);
		bd3_0.fillGrid(boxX[3],boxZ[4],boxX[4],1400,Boundary.OUTSIDE);
		bd3_0.doBorders();
		boundaries[3]=new Boundary[5];
		boundaries[3][0]=bd3_0;
		boundaries[3][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[3][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[3][3]=bd4;
		boundaries[3][4]=new Bounda(boxX[0],boxZ[0],boxX[3],boxZ[3]);
		
		
		PixelBoundaryDouble bd4_4=new PixelBoundaryDouble(nX,nZ);
		bd4_4.fillAll(Boundary.OUTSIDE);
		bd4_4.fillGrid(boxX[0],boxZ[0],500,boxZ[2],Boundary.INSIDE);
		bd4_4.doBorders();
		PixelBoundary bd4_42=new PixelBoundary(nX,nZ);
		bd4_42.fillAll(Boundary.OUTSIDE);
		bd4_42.fillGrid(boxX[0],boxZ[0],500,boxZ[2],Boundary.INSIDE);
		bd4_42.fillGrid(boxX[1],boxZ[1],500,boxZ[2],Boundary.OUTSIDE);
		bd4_42.doBorders();
		bd4_4.pb2=bd4_42;
		bd4_4.swicthY=1930;
		//bd4_4.fillGrid(250,526,boxX[2],boxZ[2],Boundary.OUTSIDE);
		//bd4_4.fillGrid(450,boxZ[1],boxX[2],boxZ[2],Boundary.OUTSIDE);
		
		PixelBoundary bd4_3=new PixelBoundary(nX,nZ);
		bd4_3.fillAll(Boundary.OUTSIDE);
		bd4_3.fillGrid(boxX[0],boxZ[1],boxX[5],boxZ[5],Boundary.INSIDE);
		bd4_3.fillGrid(boxX[4],boxZ[4],boxX[5],boxZ[5],Boundary.OUTSIDE);
		bd4_3.doBorders();
		
		PixelBoundary bd4_1=new PixelBoundary(nX,nZ);
		bd4_1.fillAll(Boundary.OUTSIDE);
		bd4_1.fillGrid(500,boxZ[0],boxX[5],boxZ[1],Boundary.INSIDE);
		bd4_1.doBorders();
		
		boundaries[4]=new Boundary[5];
		boundaries[4][0]=bd3_0;
		boundaries[4][1]=bd4_1;
		boundaries[4][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[4][3]=bd4_3;
		boundaries[4][4]=bd4_4;
		
		boundaries[5]=new Boundary[5];
		boundaries[5][0]=bd3_0;
		boundaries[5][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[5][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[5][3]=bd4;
		boundaries[5][4]=new Bounda(boxX[0],boxZ[0],boxX[3],boxZ[3]);
		
		boundaries[6]=new Boundary[4];
		boundaries[6][0]=bd3_0;
		boundaries[6][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[6][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[6][3]=bd4;
	
		
		
		boundaries[7]=new Boundary[4];
		boundaries[7][0]=bd3_0;
		boundaries[7][1]=new Bounda(boxX[0],boxZ[0],boxX[5],boxZ[1]);
		boundaries[7][2]=new Bounda(boxX[5],boxZ[0],boxX[6],boxZ[4]);
		boundaries[7][3]=bd4;
	
	}
public void copy3(VoxelStream grid,int y){
		if (y<2040||y>2200)return;
		for (int z = 0; z < nZ; z++) {
			for (int x = 0; x < nX; x++) {
				buffer3[x][z]=-10000;
			}
		}
		
		
		float f=1;
		
		
		float fZ=(f*90);
		float fX=(f*-90);
		int tZ=(int)fZ;
		int tX=(int)fX;
		
		
		int index=0;
		for (int z = 0; z < nZ; z++) {
			for (int x = 0; x < nX; x++) {
				imgCopy.pixels[index] = color(0, 255, 0, 0);
				if (maskFrom2.get(x, z) == Boundary.INSIDE) {
					//if (solids[index]) {
						int z2 = z + tZ;
						int x2 = x + tX;
						if (z2 < nZ && z2 >= 0 && x2 >= 0 && x < nX) {
							int indexNew = z2 * nX + x2;
							buffer3[x2][z2] = grid.getValue(x, y, z);
							if (!solids[indexNew]) {
							imgCopy.pixels[indexNew] = color(0, 255, 255, 100);
							}
							
						}
					}
				//}
				index++;
			}
		}
		
		imgCopy.updatePixels();
		for (int z = 0; z < nZ; z++) {
			for (int x = 0; x < nX; x++) {
				float value = buffer3[x][z];
				float cV = grid.getValue(x, y, z);
				if (value > cV) {
					grid.setValue(x, y, z, value);
				}
			}
		}
	}
	public void copy2(VoxelStream grid,int y){
		
		for (int z = 0; z < nZ; z++) {
			for (int x = 0; x < nX; x++) {
				buffer2[x][z]=-10000;
			}
		}
		
		
		float f=0;
		
		f=map(y,1300,1600,0,1);
		f=constrain(f,0,1);
		float fZ=(f*-40);
		float fX=(f*-40);
		int tZ=(int)fZ;
		int tX=(int)fX;
		float rZ=fZ-tZ;
		float rX=fX-tX;
		int tZ2=(int)fZ+1;
		int tX2=(int)fX+1;
		
		int index=0;
		for (int z = 0; z < nZ; z++) {
			for (int x = 0; x < nX; x++) {
				imgCopy.pixels[index] = color(0, 255, 0, 0);
				if (maskFrom.get(x, z) == Boundary.INSIDE) {
					//if (solids[index]) {
						int z2 = z + tZ;
						int x2 = x + tX;
						if (z2 < nZ && z2 >= 0 && x2 >= 0 && x < nX) {
							int indexNew = z2 * nX + x2;
							buffer2[x2][z2] = grid.getValue(x, y, z);
							if (!solids[indexNew]) {
							imgCopy.pixels[indexNew] = color(0, 255, 255, 100);
							}
							
						}
					}
				//}
				index++;
			}
		}
		
		imgCopy.updatePixels();
		for (int z = 0; z < nZ; z++) {
			for (int x = 0; x < nX; x++) {
				float value = buffer2[x][z];
				float cV = grid.getValue(x, y, z);
				if (value > cV) {
					grid.setValue(x, y, z, value);
				}
			}
		}
	}
	public void copy(int y){
		PixelBoundary pB=pixBoundsLevel1;
		if (iLayer!=1){
			//pB=pixBoundsLevel2;
		}
		copyGrid(vStream, y);
		loadCuts(y, solids);
		for (int x = 0; x < nX; x++) {
			for (int z = 0; z < nZ; z++) {
				if (pB.get(x, z)==Boundary.INSIDE){
					int index = this.getIndex(x, z);
					solids[index] = true;
				}
			}
		}
		
		blobInfo = blobs();
		loadCuts(y, solids);
		//Mask cMask=maskCopy;
		cBoundary=bdCopyLevel2;
		if (iLayer==1){
			println("iLayer: "+iLayer);
			cBoundary=bdCopyLevel1;
		}
		if (iLayer==2||iLayer==2){
			println("iLayer: "+iLayer);
			cBoundary=bdCopyLevel2;
		}
		
		float f=map(y,600,yCuts[2],0.5f,1);
		if (y>yCuts[3]){
			f=map(y,1500,yCuts[4],1,0);
		}
		f=this.constrain(f, 0, 1);
		
		pasteGrid(vStream, y, cBoundary,
				(int)(f*40), 0);
		pasteGrid(vStream, y,cBoundary,
				0, (int)(f*-20));
		pasteGrid(vStream, y,cBoundary,
				(int)(f*10), (int)(f*10));
	}
	public void copyParts(int y) {
		vStream.startI = y;
		vStream.initBuffer(1);
		vStream.loadAllValues();
		// translate
		copy(y);
	}
	public void VStreamToSolids(int y){
		for (int x=0;x<nX;x++){
			for (int z=0;z<nZ;z++){
				int index=this.getIndex(x,z);
				float v=vStream.getValue(x, y, z);
				if (v>=-iso)
					solids[index]=true;
				else{
					solids[index]=false;
				}
			}
		}
	}
	public void copyGrid(VoxelStream grid,int y){
		for (int x = 0; x < grid.nX; x++) {
			for (int z = 0; z <grid.nZ; z++) {
				buffer[x][z]=grid.getValue(x, y, z);
			}
		}
	}

	public void pasteGrid(VoxelStream grid, int y, PixelBoundary b3d, int tX, int tZ) {
		for (int x = (int) b3d.x1; x < b3d.x2; x++) {
			for (int z = (int) b3d.y1; z < b3d.y2; z++) {
				if (b3d.isBorder(x, z)!=Boundary.OUTSIDE){
				float value = buffer[x][z];
				if (z + tZ > 0) {
					float cV=grid.getValue(x + tX, y, z + tZ);
					if (value>cV) {
						//int index = getIndex(x, z);
						int index=(z+tZ)*nX+(x+tX);
						
						int iBlob = blobs[index];
						//blobs[index]=0;
						//imgBlobs.pixels[index]=color(0);
						//if (iBlob!=0)println("hallo Knut");
						//println("bInfo"+iBlob+": "+blobInfo[iBlob][0]);
						if (!solids[index]&&(blobInfo[iBlob][0]>(boxX[0]+10)||blobInfo[iBlob][4]==1)) {
							grid.setValue(x + tX, y, z + tZ, value);
						}
					}
				}
				}
			}
		}
		imgBlobs.updatePixels();
	}
	
	public void mirrorXGrid(float[][][]grid,Box3D b3d,int limit){
		
		int mX=(int)b3d.x2;
		for (int x=(int)b3d.x1;x<b3d.x2;x++){
			for (int y=(int)b3d.y1;y<b3d.y2;y++){
				for (int z=(int)b3d.z1;z<b3d.z2;z++){
					
					if (grid[x][y][z]>-iso){
						float mirrorX=(mX-x)+mX;
						if (mirrorX<limit){
						grid[(mX-x)+mX][y][z]=grid[x][y][z];
						}
					}
				}
			}
		}
	}
	public float[][][] mirrorYGrid(float[][][]grid,Box3D b3d,float my,int limit){
//		float[][][] mirrorGrid=new float[][][];
//		
//		
//		for (int x=(int)b3d.x1;x<b3d.x2;x++){
//			for (int y=(int)b3d.y1;y<b3d.y2;y++){
//				for (int z=(int)b3d.z1;z<b3d.z2;z++){
//					
//					if (grid[x][y][z]>-iso){
//						float mY=(mX-x)+mX;
//						if (mirrorX<limit){
//						grid[(mX-x)+mX][y][z]=grid[x][y][z];
//						}
//					}
//				}
//			}
//		}
		return null;
	}
	public void makeSolid(int y1,int y2){
		int offset=20;
		for (int y=y1;y<y2;y++){
			println("y: "+y);
			
			loadCuts(y,solids);
			if (y>=677){
				copyParts( y);
				copy2(vStream,y);
				copy3(vStream,y);
				VStreamToSolids(y);
			}
			imgOffset=this.loadImage(getPathOffset(y));
			imgOffset.loadPixels();
			blobInfo= blobs();
			
			/*for (int i=0;i<offsetBoxes.size();i++){
				Box3D b=offsetBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					offsetthinLines(b,offset);
				}
			}*/
			
			for (int i=0;i<offsetBoxes2.size();i++){
				Box3D b=offsetBoxes2.get(i);
				if (b.y1<=y&&b.y2>=y){
					offsetthinLines2(b,offset);
				}
			}
			
			for (int i=0;i<blobBoxes.size();i++){
				Box3D b=blobBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillBobs3D(b);
				}
			}
			
			for (int i=0;i<solidBoxes.size();i++){
				Box3D b=solidBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillSolid(b);
				}
			}
			
			for (int i=0;i<solidBlobBoxes.size();i++){
				Box3D b=solidBlobBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillSolidArray(b);
				}
			}
			
			blobInfo= blobs();
			for (int i=0;i<blobBoxes2.size();i++){
				Box3D b=blobBoxes2.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillBobs3D(b);
				}
			}
			
			loadCuts(y,solids);
			for (int i=0;i<solidBlobBoxes3.size();i++){
				Box3D b=solidBlobBoxes3.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillSolidArray(b);
				}
			}
			for (int i=0;i<this.fillAndSolidBoxes.size();i++){
				Box3D b=fillAndSolidBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillAndSolid(b);
				}
			}
			blobInfo= blobs();
			for (int i=0;i<blobBoxes3.size();i++){
				Box3D b=blobBoxes3.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillBobs3D(b);
				}
			}
			for (int i=0;i<subSolidBoxes.size();i++){
				Box3D b=subSolidBoxes.get(i);
				if (b.y1<=y&&b.y2>=y){
					fillBobs3D(b);
				}
			}
			imgOffset.updatePixels();
			imgOffset.save(getPathOffset(y));
		}
	}
	
	public void initTextGraphics(){
		 textGraphics=this.createGraphics(300,50,P2D);
		 myFont = createFont("Arial", textGraphics.height);
		
	}
	// z1=497
	public void initOffsetImages(){
		for (int y=bdY1;y<bdY2;y++){
			//
			String offsetPath=getPathOffset(y);
			File file=new File(offsetPath);
			if (!file.exists()){
				println("image y recreated: "+y);
				PImage img=this.createImage(nX,nZ,ARGB);
				img.save(offsetPath);
			}
			
		}
	}
	public void rebuildImages(int y1,int y2){
		for (int y=y1;y<y2;y++){
			println("y: "+y);
			String offsetPath=getPathOffset(y);
			File file=new File(offsetPath);
			
				PImage img=this.createImage(nX,nZ,ARGB);
				img.save(offsetPath);
			
			
		}
	}
	public void offsetthinLines(Box3D box,float offset,int x,int y){
		int[] coords=new int[]{x,y};
		offsetthinLines(box,offset,coords);
	}
	
	public void fillBobs3D(Box3D box) {
		// find blobs complete inside the box.
		// fills the white blobs
		for (int i = 0; i < blobInfo.length; i++) {
			int[] blob = blobInfo[i];
			if (blob[0] > box.x1 && blob[2] < box.x2 && blob[1] > box.z1
					&& blob[3] < box.z2) {
				for (int j = 0; j < blobs.length; j++) {
					if (blobs[j] == i && !solids[j]) {
						imgOffset.pixels[j] = cyan;
					}
				}
			}
		}
	}
	public void fillSolidArray(Box3D box) {
		// makes Solids
		for (int x=(int)box.x1;x<box.x2;x++){
			for (int y=(int)box.z1;y<box.z2;y++){
				int index=getIndex(x,y);
				solids[index]=true;
			}
		}		
	}
	public void fillAndSolid(Box3D box) {
		for (int x=(int)box.x1;x<box.x2;x++){
			for (int y=(int)box.z1;y<box.z2;y++){
				int index=getIndex(x,y);
				imgOffset.pixels[index] = cyan;
				solids[index]=true;
			}
		}		
	}
	
	public void fillSolid(Box3D box) {
		for (int x=(int)box.x1;x<box.x2;x++){
			for (int y=(int)box.z1;y<box.z2;y++){
				int index=getIndex(x,y);
				if (!solids[index]){
					imgOffset.pixels[index] = cyan;
				}
			}
		}		
	}
	public void offsetthinLines(Box3D box,float offset){
		int cI=0;
		coords=new int[(int)(offsetBox.z2-offsetBox.z1-1)*2];
		for (int z=(int)offsetBox.z1+1;z<offsetBox.z2;z++){
			coords[cI]=(int)offsetBox.x1+1;
			coords[cI+1]=(int)z;
			cI+=2;
		}
		offsetthinLines( box, offset,coords);
	}
	public void offsetthinLines2(Box3D box,float offset){
		int cI=0;
		coords=new int[(int)(offsetBox.z2-offsetBox.z1-1)*2];
		for (int z=(int)offsetBox.z1+1;z<offsetBox.z2;z++){
			coords[cI]=(int)offsetBox.x1+1;
			coords[cI+1]=(int)z;
			cI+=2;
		}
		offsetthinLines2( box, offset,coords);
	}

	public void offsetthinLines2(Box3D box, float offset, int[] coords) {
		int x1 = (int) box.x1;
		int x2 = (int) box.x2;
		int y1 = (int) box.z1;
		int y2 = (int) box.z2;
		float[] offests = new float[solids.length];
		Arrays.fill(offests, 10000);
		
		for (int i = 0; i < blobInfo.length; i++) {
			int[] blob = blobInfo[i];
			if (blob[0] >= box.x1 && blob[2] <= box.x2 && blob[1] >= box.z1
					&& blob[3] <= box.z2) {
				boolean contains=false;
				for (int j=0;j<coords.length;j+=2){
					int index=this.getIndex(coords[j], coords[j+1]);
					if (blobs[index]==i){
						contains=true;
					}
				}
				if (contains){
				for (int j = 0; j < blobs.length; j++) {
					if (blobs[j] == i) {
						if (solids[j] == true)
							break;
						offests[j] = 0;
					}
				}
				}
			}
		}

		int nNew = 0;

		int alpha = color(255, 0);
		offests = DepthAnalyser2D.getOffset(offests, nZ, nX, offset);
		
		// 0-offset = 0-iso = 0-100
		 
		for (int i = 0; i < offests.length; i++) {
			int x = getXInt(i);
			int y = getYInt(i);
			if (offests[i] < 10000 && offests[i] > 0) {
				if (x > x1 && x < x2 && y > y1 && y < y2) {
					imgOffset.pixels[i] = cyan;
					float cv=map(offests[i],0,offset,0,100);
					imgOffset.pixels[i]=color(0,cv,0);
				}
			}
		}

	}
	public void offsetthinLines(Box3D box,float offset,int[] coords){
		
		int[]candidates=new int[solids.length];
		int[]newCandidates=new int[solids.length];
		boolean[]blob=new boolean[solids.length];
		int nNew=0;
		for (int i=0;i<coords.length;i+=2){
			int cIndex=getIndex(coords[i],coords[i+1]);
			if (!solids[cIndex]){
				candidates[nNew]=cIndex;
				blob[cIndex]=true;
				nNew++;
			}
		}
		println("nNew: "+nNew);
		int x1=(int)box.x1;
		int x2=(int)box.x2;
		int y1=(int)box.z1;
		int y2=(int)box.z2;
		while (nNew>0){
			int nCandidates=nNew;
			nNew=0;
			for (int i=0;i<nCandidates;i++){
				int cand=candidates[i];
				
				int x= getXInt(cand);
				int y =getYInt(cand);
				
				if (x>x1&&x<x2&&y>y1&&y<y2){
					int leftI=cand-1;
				if (x>=x1&&!solids[leftI]&&!blob[leftI]){
					blob[leftI]=true;
					newCandidates[nNew]=leftI;
					nNew++;
				}
				int rightI=cand+1;
				x= (int) (rightI * 1f % nX);
				if (x<x2&&!solids[rightI]&&!blob[rightI]){
					blob[rightI]=true;
					newCandidates[nNew]=rightI;
					nNew++;
				}
				int topI=cand+nX;
				y = (int)(topI *1f/ nX);
				if (y>=y1&&!solids[topI]&&!blob[topI]){
					blob[topI]=true;
					newCandidates[nNew]=topI;
					nNew++;
				}
				int bottomI=cand-nX;
				y = (int)(bottomI *1f/ nX);
				if (y<y2&&!solids[bottomI]&&!blob[bottomI]){
					blob[bottomI]=true;
					newCandidates[nNew]=bottomI;
					nNew++;
				}
				}
			}
			for (int i=0;i<nNew;i++){
				
				candidates[i]=newCandidates[i];
			}
		}
		float[] offests=new float[blob.length];
		for (int i=0;i<blob.length;i++){
			if (blob[i]){
				offests[i]=0;
			}
			else{
				offests[i]=10000;
			}
		}
		
		
		int alpha=color(255,0);
		offests=DepthAnalyser2D.getOffset(offests,nZ,nX,offset);
		for (int i=0;i<offests.length;i++){
			int x=  getXInt(i);
			int y = getYInt(i);
			if (offests[i]<10000&&offests[i]>0){
				if (x>x1&&x<x2&&y>y1&&y<y2){
					float cv=map(offests[i],0,offset,0,100);
					imgOffset.pixels[i]=color(0,cv,0);
					//imgOffset.pixels[i]=cyan;
				}
			}
		}
		
	}
	public void setupMenu() {
		Frame frame=findFrame();
		MenuListener myMenuListener=new MenuListener();
		MenuBar myMenu = new MenuBar();
		Menu topButton = new Menu("Box");
		MenuItem item1 = new MenuItem("New Box");
		topButton.add(item1);
		item1.addActionListener(myMenuListener);
		MenuItem item2 = new MenuItem("Delete Box");
		item2.addActionListener(myMenuListener);
		topButton.add(item2);
		myMenu.add(topButton);
		Menu topButton2 = new Menu("Display");
		myMenu.add(topButton2);
		MenuItem men02item1 = new MenuItem("Display Boxes");
		men02item1.addActionListener(myMenuListener);
		topButton2.add(men02item1);
		
		MenuItem men02item2 = new MenuItem("Display Grid");
		men02item2.addActionListener(myMenuListener);
		topButton2.add(men02item2);
		frame.setMenuBar(myMenu);
	}
	
	public Frame findFrame() {
		Container f = this.getParent();
		while (!(f instanceof Frame) && f != null)
			f = f.getParent();
		return (Frame) f;
	}
	
	public void setupGUI(){
		cam=new PCam2DGraphics(this);
		cam.setBounds(0-20, 0-20, nX+20, nZ+20);
		gui = new Gui(this, this.createDefaultFont(12));
		spinnerFrom=gui.addGuiSpinnerGroup(1100, 100, 56, 56, bdY2-1);
		spinnerTo=gui.addGuiSpinnerGroup(1170, 100, 123, 56, bdY2-1);
		gui.addGuiLabel(20, 80,  "Box");
		spX1=gui.addGuiSpinnerGroup(20, 100, 123, 56, 3055);
		spX2=gui.addGuiSpinnerGroup(100, 100, 123, 56, 3055);
		spY1=gui.addGuiSpinnerGroup(20, 120, 123, 56, bdY2-1);
		spY2=gui.addGuiSpinnerGroup(100, 120, 123, 56, bdY2-1);
		spZ1=gui.addGuiSpinnerGroup(20, 140, 123, 56, 3055);
		spZ2=gui.addGuiSpinnerGroup(100, 140, 123, 56, 3055);
	}
	public void setSelectedBox(Box3D b){
		spX1.setValue((int)b.x1);
		spY1.setValue((int)b.y1);
		spZ1.setValue((int)b.z1);
		spX2.setValue((int)b.x2);
		spY2.setValue((int)b.y2);
		spZ2.setValue((int)b.z2);
		currentBox=b;
	}
	public void loadBoxes(String path){
		fillBoxes=new ArrayList<Box3D>();
		String[]lines=this.loadStrings(path);
		for (int i=0;i<lines.length;i++){
			String[]ccords=lines[i].split(",");
			int[]cs=new int[ccords.length];
			for (int j=0;j<ccords.length;j++){
				cs[j]=Float.valueOf(ccords[j]).intValue();
			}
			fillBoxes.add(new Box3D(cs[0],cs[1],cs[2],cs[3],cs[4],cs[5]));
		}
		println("loaded");
	}
	public void saveBoxes(String path){
		String[]lines=new String[fillBoxes.size()];
		for (int i=0;i<fillBoxes.size();i++){
			Box3D b=fillBoxes.get(i);
			lines[i]=b.x1+","+b.y1+","+b.z1+","+b.x2+","+b.y2+","+b.z2;
		}
		this.saveStrings(path, lines);
		println("saved");
	}
	
	public int[][] blobs() {
		Arrays.fill(blobs,-1);
		int x1 = 0;
		int y1 = 0;
		int x2 = nX;
		int y2 = nZ;
		int iBlob = 0;
		for (int j = 0; j < solids.length; j++) {
			if (blobs[j] == -1) {
				blobs[j] = iBlob;
				boolean cCol = solids[j];
				Arrays.fill(candidates, 0);
				candidates[0]=j;
				Arrays.fill(newCandidates, 0);
				int nNew = 1;
				int size=0;
				while (nNew > 0) {
					int nCandidates = nNew;
					nNew = 0;
					for (int i = 0; i < nCandidates; i++) {
						int cand = candidates[i];
						int x = (int) (cand * 1f % nX);
						int y = (int) (cand * 1f / nX);
						int leftI = cand - 1;
						if (x > x1 && solids[leftI] == cCol
								&& blobs[leftI] == -1) {
							blobs[leftI] = iBlob;
							newCandidates[nNew] = leftI;
							nNew++;
						}
						int rightI = cand + 1;
						if (x < x2 - 1 && solids[rightI] == cCol
								&& blobs[rightI] == -1) {
							blobs[rightI] = iBlob;
							newCandidates[nNew] = rightI;
							nNew++;
						}
						int topI = cand + nX;
						if (y < y2 - 1 && solids[topI] == cCol
								&& blobs[topI] == -1) {
							blobs[topI] = iBlob;
							newCandidates[nNew] = topI;
							nNew++;
						}
						int bottomI = cand - nX;
						
						if (y > y1  && solids[bottomI] == cCol
								&& blobs[bottomI] == -1) {
							blobs[bottomI] = iBlob;
							newCandidates[nNew] = bottomI;
							nNew++;
						}
					}
					size+=nNew;
					for (int i=0;i<nNew;i++){
						candidates[i]=newCandidates[i];
					}
					
				}
				iBlob++;
			}
		}
		for (int x=0;x<nX;x++){
			for (int y=0;y<nZ;y++){
				if (x<bdX1||x>bdX2||y<bdZ1||y>bdZ2){
					int index=getIndex(x,y);
					blobs[index] = iBlob;
				}
			}
		}
		iBlob++;
		int[][]blobInfo=new int[iBlob][5];
		for (int i=0;i<blobInfo.length;i++){
			blobInfo[i][0]=1000000;
			blobInfo[i][1]=1000000;
			blobInfo[i][2]=-1000000;
			blobInfo[i][3]=-1000000;
		}
		
		for (int i=0;i<blobs.length;i++){
			int iBl=blobs[i];
			int x = (int) (i * 1f % nX);
			int y = (int) (i * 1f / nX);
			blobInfo[iBl][0]=min(blobInfo[iBl][0],x);
			blobInfo[iBl][1]=min(blobInfo[iBl][1],y);
			blobInfo[iBl][2]=max(blobInfo[iBl][2],x);
			blobInfo[iBl][3]=max(blobInfo[iBl][3],y);
			if (solids[i]){
				blobInfo[iBl][4]=1;
			}
		}
		
		println("iBlob: "+iBlob);
		colorMode(HSB,255);
		for (int i=0;i<blobs.length;i++){
			imgBlobs.pixels[i]=color(255f*blobs[i]/blobInfo.length,255,255);
		}
		colorMode(RGB,255);
		imgBlobs.updatePixels();
		return blobInfo;
	}
	//calculate blobs
	
	public void fill(int x,int y){
		int[]candidates=new int[solids.length];
		int[]newCandidates=new int[solids.length];
		int nNew=1;
		candidates[0]=getIndex(x,y);
		int setCol=color(255,255,0);
		boolean setSolid=false;
		if (imgNew.pixels[getIndex(x,y)]==setCol){
			setSolid=false;
			setCol=color(0);
			println("fill inverse");
		}
		
		imgNew.pixels[getIndex(x,y)]=setCol;
		
		while (nNew>0){
			int nCandidates=nNew;
			nNew=0;
			for (int i=0;i<nCandidates;i++){
				int cand=candidates[i];
				int leftI=cand-1;
				if (x>=0&&solids[leftI]==setSolid&&imgNew.pixels[leftI]!=setCol){
					imgNew.pixels[leftI]=setCol;
					newCandidates[nNew]=leftI;
					nNew++;
				}
				int rightI=cand+1;
				if (x<nX-1&&solids[rightI]==setSolid&&imgNew.pixels[rightI]!=setCol){
					imgNew.pixels[rightI]=setCol;
					newCandidates[nNew]=rightI;
					nNew++;
				}
				int topI=cand+nX;
				if (y>=0&&solids[topI]==setSolid&&imgNew.pixels[topI]!=setCol){
					imgNew.pixels[topI]=setCol;
					newCandidates[nNew]=topI;
					nNew++;
				}
				int bottomI=cand-nX;
				if (y<nZ-1&&solids[bottomI]==setSolid&&imgNew.pixels[bottomI]!=setCol){
					imgNew.pixels[bottomI]=setCol;
					newCandidates[nNew]=bottomI;
					nNew++;
				}
			}
			for (int i=0;i<nNew;i++){
				candidates[i]=newCandidates[i];
			}
		}
		
		
		imgNew.updatePixels();
	}
	public void writeToImage(boolean[] inside,PImage img){
		int white=color(255);
		int black=color(0,0);
		
		for (int i = 0; i < img.pixels.length; i++) {
			int cZ=i/nX;
			int cX=i%nX;
			
			if (inside[i]&&cX>bdX1&&cX<bdX2&&cZ>bdZ1&&cZ<bdZ2) {
				img.pixels[i]=white;
			}
			else{
				img.pixels[i]=black;
			}
		}
		img.updatePixels();
	}
	
	
	
	
	public void loadCuts(int y, boolean[] inside) {
		img=this.loadImage(getPath(y));
		for (int i = 0; i < img.pixels.length; i++) {
			int col = img.pixels[i];
			if (brightness(col) > 0) {
				inside[i] = true;
			}
			else{
				inside[i] = false;
			}
		}
		int colAlpha=color(255,0);
		for (int i = 0; i < imgNew.pixels.length; i++) {
			imgNew.pixels[i]=colAlpha;
		}
		imgNew.updatePixels();
	}
	
	public String getPath(int i){
		return pathCuts+"/solid"+(10000+i)+".png";
	}
	public String getPathNew(int i){
		return pathFixed+"/fixed"+(10000+i)+".png";
	}
	public String getPathOffset(int i){
		return pathOffset+"/offset"+(10000+i)+".png";
	}
	public void addBox(int x1,int y1,int z1,int x2,int y2,int z2){
		currentBox=new Box3D(x1,y1,z1,x2,y2,z2);
		fillBoxes.add(currentBox);
		setSelectedBox(currentBox);
	}
	
	public void loadBoxValues(Box3D b){
		b.setX1(spX1.getValue());
		b.setY1(spY1.getValue());
		b.setZ1(spZ1.getValue());
		b.setX2(spX2.getValue());
		b.setY2(spY2.getValue());
		b.setZ2(spZ2.getValue());
	}
	public int chooseLayer(int cY){
		for (int i=0;i<yCuts.length;i++){
			if (yCuts[i]>cY)return i;
		}
		return yCuts.length;
	}
	public void draw() {
		int newY=spinnerFrom.getValue();
		if (currentBox!=null){
			loadBoxValues(currentBox);
		}
		
		if (newY!=cY){
			cY=newY;
			iLayer=chooseLayer(cY);
			loadCuts(cY,solids);
			copyParts(cY);
			copy2(vStream,cY);
			copy3(vStream,cY);
			VStreamToSolids(cY);
			writeToImage(solids,imgCanvas);
			
			imgOffset=this.loadImage(getPathOffset(cY));
			imgOffset.loadPixels();
			int iB=0;
			for (int i=0;i<imgOffset.pixels.length;i++){
				int col=imgOffset.pixels[i];
				if (brightness(col)>100)iB++;
			}
		}
		if (flagSaveBoxes){
			flagSaveBoxes=false;
			saveBoxes(pathBoxes);
		}
		if (flagAddBox){
			addBox(200,cY,200,nX-200,cY+1,nZ-200);
			println("box added");
			flagAddBox=false;
		}
		if (flagDeleteBox){
			flagDeleteBox=false;
			if (currentBox!=null){
				fillBoxes.remove(currentBox);
				currentBox=null;
			}
			
		}
		background(0);
		drawData();
		drawGui();
		
	}
	
	public void drawGui(){
		fill(10);
		stroke(100);
		rect(0,0,300,height);
		rect(1100,0,300,height);
		gui.display(g);
	}

	public void drawData() {
		g.pushMatrix();
		cam.useCam(g);
		if (frameCount == 1) {
			cam.zoomToBounds();
		}
		//
		int cSeg = 0;
		for (int i = 0; i < yCuts.length; i++) {
			if (yCuts[i] < cY)
				cSeg = i;
		}
		background(cSeg * 30, 0, 0);
		//image(imgBlobs, 0, 0);
		image(imgNew, 0, 0);
		image(imgOffset, 0, 0);
		image(imgCanvas, 0, 0);
		
		
		noFill();
		stroke(255, 0, 0);
		for (int i = 0; i < boundaries[0].length; i++) {
			Boundary bd = boundaries[0][i];
			rect(bd.x1(), bd.y1(), bd.x2() - bd.x1(), bd.y2() - bd.y1());
		}
		if (displayBoxes) {
			stroke(0, 255, 255);
			for (int i = 0; i < allBoxes.size(); i++) {
				Box3D b = allBoxes.get(i);
				drawBox(b);
			}
			if (currentBox != null) {
				strokeWeight(1f);
				drawBox(currentBox);
			}
		}
		for (int i = 0; i < solidBoxes.size(); i++) {
			Box3D b = solidBoxes.get(i);
			drawBox(b);
		}
		
		strokeWeight(1f);
		stroke(255, 0, 255);
		rect(xK(bdsFloat.x1), zK(bdsFloat.z1), (bdsFloat.x2 - bdsFloat.x1)/dim, (bdsFloat.z2 - bdsFloat.z1)/dim);
		stroke(0, 255, 255);
		// rect(offsetBox.x1,offsetBox.z1,offsetBox.x2-offsetBox.x1,offsetBox.z2-offsetBox.z1);
		stroke(0, 255, 0);
		if (displayGrid) {
		for (int i = 0; i < xSegs.length; i++) {
			float x1 = xK(xSegs[i]);
			line(x1, bdZ1, x1, bdZ2);
		}
		for (int i = 0; i < zSegs.length; i++) {
			float z1 = zK(zSegs[i]);
			line(bdX1, z1, bdX2, z1);
		}
		}
		stroke(0, 255,255);
		/*Polygon poly=test.poly;
		beginShape();
		for (int i=0;i<poly.npoints;i++){
			vertex(poly.xpoints[i],poly.ypoints[i]);
		}
		endShape(CLOSE);*/
		
		for (int i=0;i<splitLines.size();i++){
			Line3D l=splitLines.get(i);
			if (l.y1<=cY&&l.y2>=cY){
				line(l.x1,l.z1,l.x2,l.z2);
			}
		}
		
		//background(255);
		/*stroke(255);
		rectMode(CORNER);
		for (int i=0;i<blobInfo.length;i++){
			//println("left: "+blobInfo[i][0]);
			rect(blobInfo[i][0],blobInfo[i][1],blobInfo[i][2]-blobInfo[i][0],blobInfo[i][3]-blobInfo[i][1]);
		}*/
		stroke(0, 255, 255);
		 //rect(maskCopyLevel2.x1,maskCopyLevel2.y1,maskCopyLevel2.x2-maskCopyLevel2.x1,maskCopyLevel2.y2-maskCopyLevel2.y1);
		
		imgMask.loadPixels();
		int index=0;
		for (int y=0;y<nZ;y++){
		for (int x=0;x<nX;x++){
				if (maskFrom2.get(x, y)!=Boundary.OUTSIDE){
					imgMask.pixels[index]=color(0,0,255,100);
				}
				index++;
			}
		}
		
		imgMask.updatePixels();
		image(imgMask, 0, 0);
		image(imgCopy, 0, 0);
		g.popMatrix();
		
		
	}

	public void drawBox(Box3D b){
		if (cY>=b.getY1()&&cY<=b.getY2()){
			rect(b.x1,b.z1,b.x2-b.x1,b.z2-b.z1);
		}
	}
	public void stamptext(String text){
		textGraphics.beginDraw();
		 textGraphics.background(255);
		 textGraphics.textFont(myFont);
		 textGraphics.fill(0);
		 textGraphics.text(text,0,textGraphics.height);
		 textGraphics.endDraw();
	}
	public void stamptext(String text,int x,int y ,int z,FloatGrid3D grid,int dZ){
		 textGraphics.beginDraw();
		 textGraphics.background(255);
		 textGraphics.textFont(myFont);
		 textGraphics.fill(0);
		 textGraphics.text(text,0,textGraphics.height);
		 textGraphics.endDraw();
		 for (int xK=0;xK<textGraphics.width;xK++){
			 for (int yK=0;yK<textGraphics.height;yK++){
				 int col=textGraphics.get(xK, yK);
				 if (brightness(col)<200){
					 for (int zK=0;zK<dZ;zK++){
						 grid.set(xK+x, (textGraphics.height-yK)+y, zK+z, iso*2f);// vertical mirror
					 }
				 }
			 }
		 }
	}
	public void writeInt(int number,float x,float y ,float z,FloatGrid3D grid,FloatGrid3D depth,float sY,Boundary bounds){
		Box3D[]boxes=new Box3D[7];//3 vertical //  4 horizontal
		float dimX=0.03f;
		float dimY=0.05f;
		float dimZ=0.005f;
		float x0=x;
		float x1=x+dimX*0.33f;
		float x2=x+dimX*0.66f;
		float x3=x+dimX;
		
		float y0=y;
		float y1=y+dimY*0.2f;
		float y2=y+dimY*0.4f;
		float y3=y+dimY*0.6f;
		float y4=y+dimY*0.8f;
		float y5=y+dimY;
		
		float z0=z;
		float z1=z+dimZ;
	
		
		boxes[0]=new Box3D(x0,y4,z0,x3,y5,z1);
		boxes[1]=new Box3D(x0,y2,z0,x3,y3,z1);
		boxes[2]=new Box3D(x0,y0,z0,x3,y1,z1);
		boxes[3]=new Box3D(x0,y2,z0,x1,y5,z1);
		boxes[4]=new Box3D(x2,y2,z0,x3,y5,z1);
		boxes[5]=new Box3D(x0,y0,z0,x1,y3,z1);
		boxes[6]=new Box3D(x2,y0,z0,x3,y3,z1);
		int[] boxIds=null;
		if ( number==0)
			boxIds=new int[]{0,2,3,4,5,6};
		else if (number==1)
			boxIds=new int[]{4,6};
		else if (number==2)
			boxIds=new int[]{0,1,2,4,5};
		else if (number==3)
			boxIds=new int[]{0,1,2,4,6};
		else if (number==4)
			boxIds=new int[]{1,3,4,6};
		else if (number==5)
			boxIds=new int[]{0,1,2,3,6};
		else if (number==6)
			boxIds=new int[]{0,1,2,3,5,6};
		else if (number==7)
			boxIds=new int[]{0,4,6};
		else if (number==8)
			boxIds=new int[]{0,1,2,3,4,5,6};
		else if (number==9)
			boxIds=new int[]{0,1,2,3,4,6};
		for (int i=0;i<boxIds.length;i++){
			subBox(boxes[boxIds[i]],grid,depth,sY,bounds);
		}
	}
	boolean isBoxBorder(float x,float z,float border){
		if (x>xSegs[xSegs.length-1]-border)return true;
		
		if (z<zSegs[0]+border||z>zSegs[zSegs.length-1]-border)return true;
		return false;
	}
	
	public void subBox(Box3D box,FloatGrid3D grid,FloatGrid3D depth,float sY,Boundary bounds){
		subBox(box.x1,box.y1,box.z1,box.x2,box.y2,box.z2,grid,depth,sY,bounds);
	}
	public void subBoxPriority(float x1,float y1,float z1,float x2,float y2,float z2,FloatGrid3D grid,FloatGrid3D depth,float sY,Boundary bounds){
		int ix1=this.xK(x1)/step;
		int ix2=this.xK(x2)/step;
		int iy1=((int)((y1-sY)/dim))/step;
		int iy2=((int)((y2-sY)/dim))/step;
		int iz1=this.zK(z1)/step;
		int iz2=this.zK(z2)/step;
		
		iz1=max(0,iz1);
		iz2=min(nZ/step,iz2);
		ix1=max(0,ix1);
		ix2=min(nX/step,ix2);
		iy1=max(0,iy1);
		iy2=min(grid.nY,iy2);
		
		// todo differentiate it with the box borders...
		
		
		float offset = 0.02f / (dim * step);
		for (int x=ix1;x<ix2;x++){
			for (int y=iy1;y<iy2;y++){
				for (int z=iz1;z<iz2;z++){
					if (getX(x)>xSegs[0]+0.02f){
					if (bounds.isBorder(x*step,y, z*step)!=Boundary.OUTSIDE){
					
						grid.set(x, y, z, iso*2f);//outside
						
					
					}
					}
				}
			}
		}
	}
	public void subBox(float x1,float y1,float z1,float x2,float y2,float z2,FloatGrid3D grid,FloatGrid3D depth,float sY,Boundary bounds){
		int ix1=this.xK(x1)/step;
		int ix2=this.xK(x2)/step;
		int iy1=((int)((y1-sY)/dim))/step;
		int iy2=((int)((y2-sY)/dim))/step;
		int iz1=this.zK(z1)/step;
		int iz2=this.zK(z2)/step;
		
		iz1=max(0,iz1);
		iz2=min(nZ/step,iz2);
		ix1=max(0,ix1);
		ix2=min(nX/step,ix2);
		iy1=max(0,iy1);
		iy2=min(grid.nY,iy2);
		
		// todo differentiate it with the box borders...
		
		
		float offset = 0.02f / (dim * step);
		for (int x=ix1;x<ix2;x++){
			for (int y=iy1;y<iy2;y++){
				for (int z=iz1;z<iz2;z++){
					if (getX(x)>xSegs[0]+0.02f){
					if (bounds.isBorder(x*step,y, z*step)!=Boundary.OUTSIDE){
					if (depth.get(x,y,z)>offset){
						grid.set(x, y, z, isoOut);//outside
						
					}
					}
					}
				}
			}
		}
	}
	
	public void addBox(float x1,float y1,float z1,float x2,float y2,float z2,FloatGrid3D grid,FloatGrid3D depth,float sY,Boundary bounds){
		int ix1=this.xK(x1)/step;
		int ix2=this.xK(x2)/step;
		int iy1=((int)((y1-sY)/dim))/step;
		int iy2=((int)((y2-sY)/dim))/step;
		int iz1=this.zK(z1)/step;
		int iz2=this.zK(z2)/step;
		
		iz1=max(0,iz1);
		iz2=min(nZ/step,iz2);
		ix1=max(0,ix1);
		ix2=min(nX/step,ix2);
		iy1=max(0,iy1);
		iy2=min(grid.nY,iy2);
		float offset = 0.02f / (dim * step);
		for (int x=ix1;x<ix2;x++){
			for (int y=iy1;y<iy2;y++){
				for (int z=iz1;z<iz2;z++){
					if (bounds.isBorder(x * step,y+(int)(sY/dim), z * step)!=Boundary.OUTSIDE) {
						if (depth.get(x,y,z) > offset) {
							grid.set(x, y, z, 0);// inside
						}
					}
				}
			}
		}
	}
	
	public void saveObjLevelFast(int y1, int y2, Boundary[] bounds, String path) {
		//int offset
		
		int yMinus=0;
		float yMinusFLoat=yMinus*dim;
		y1 -= 1;
		
		y2 += 1;
		vStream.startI = y1;
		vStream.initBuffer(y2 - y1);
		vStream.loadAllValues();
		//translate
		
		//bring vstream to solids
			
		// load corected geometry
		FloatGrid3D grid=new FloatGrid3D((int) (nX * 1f / step),(int) ((y2 - y1) * 1f / step),(int) (nZ * 1f / step));
		for (int y = 0; y < grid.nY; y++) {
			copy(y * step + y1);
			copy2(vStream,y * step + y1);
			copy3(vStream,y * step + y1);
			imgOffset = this.loadImage(getPathOffset(y * step + y1));
			imgOffset.loadPixels();
			for (int x = 0; x < grid.nX; x++) {
				for (int z = 0; z < grid.nZ; z++) {
					int col = imgOffset.pixels[getIndex(x * step, z * step)];
					if (alpha(col) > 100) {
						if (vStream.getValue(x * step, (y * step + y1), z
								* step) < -iso) {
							vStream.setValue(x * step, (y * step + y1), z
									* step, 0);
						}
					}
				}
			}
		}
		vStream.fillValueGrid(y1, y2, grid, step);
		
		
		FloatGrid3D depth=new FloatGrid3D(grid.nX,grid.nY,grid.nZ);
		DepthAnalyser3D dA = new DepthAnalyser3D();
		for (int i=0;i<grid.values.length;i++){
			if (grid.values[i]>-iso)
				depth.values[i] = 1000;
			else 
				depth.values[i] = 0;
		}
		dA.calculateDepth(depth.values, depth.values,depth.nX,depth.nY,depth.nZ);
		
		
		for (int i = 0; i < bounds.length; i++) {
			vStream.fillValueGrid(y1, y2, grid, step);
			Boundary bd = bounds[i];
			//Box3D b3d = new Box3D(200, yCuts[4], 180, 940, yCuts[5], 560);
			//Box3D b3dAdapt = new Box3D(b3d.x1 / step, 0, b3d.z1 / step, b3d.x2
					/// step, grid.nY, b3d.z2 / step);
			// if (y1<=yCuts[4]&&y2>=)
			// this.mirrorXGrid(grid, b3dAdapt,bdX2/step-3);
			
		
			for (int x = 0; x < grid.nX; x++) {
				for (int y = 0; y < grid.nY; y++) {
					for (int z = 0; z < grid.nZ; z++) {
						float c = grid.get(x, y, z);
						int cX=x*step;
						int cY=y*step;
						int inside = bd.isBorder(x * step,y * step + y1, z * step);
						if (inside != Boundary.OUTSIDE) {
							// inside border
							if ((inside == Boundary.BORDER || y == 1
									|| y == grid.nY - 2) && (z!=boxZ[6]||i!=4)) {
								// boxZ[6]==mirrorAxis || box 2 exception...
								if (c < 0) c = c * -1f;// reached
								else if (c > 0) c = 0;// not reached
								//else if (c > iso) c = 0;// not reached
								
							} else {
								// inside volume
								if (c < 0) c = c * -1f;// reached	
								else if (c > iso)c = isoIn;// not reached
							}
						} else {
							c = isoOut;// reached
						}
						grid.set(x,y,z, c);
					}
				}
			}

			float offset = 0.04f / (dim * step);
			for (int x = 0; x < grid.nX; x++) {
				for (int y = 0; y < grid.nY; y++) {
					for (int z = 0; z < grid.nZ; z++) {
						if (depth.get(x, y, z)> offset) {
							grid.set(x, y, z, isoOut);// outside
						}
					}
				}
			}

			//Boundaries
			float by1 = getY(y1);
			float by2 = getY(y2)-yMinusFLoat;
			float bx1 = getX((int) bd.x1());
			float bx2 = getX((int) bd.x2());
			float bz1 = getZ((int) bd.y1());
			float bz2 = getZ((int) bd.y2());
			this.addBox(bx1, by1, bz1, bx1+0.04f, by2, bz2, grid, depth, by1, bd);
			this.addBox(bx2-0.04f, by1, bz1, bx2, by2, bz2, grid, depth, by1, bd);
			this.addBox(bx1, by1, bz1, bx2, by2, bz1+0.04f, grid, depth, by1, bd);
			this.addBox(bx1, by1, bz2-0.04f, bx2, by2, bz2, grid, depth,by1, bd);
			
			// segments
			for (int ii = 1; ii < xSegs.length - 1; ii++) {
				float x1 = xSegs[ii] - 0.02f;
				float x2 = xSegs[ii] + 0.02f;
				this.addBox(x1, by1, bz1, x2,by2, bz2, grid, depth, by1,bd);
			}
			for (int ii = 1; ii < zSegs.length - 1; ii++) {
				float z1 = zSegs[ii] - 0.02f;
				float z2 = zSegs[ii] + 0.02f;
				this.addBox(bx1, by1, z1, bx2,by2, z2, grid, depth, by1,bd);
			}
			// stampNumber
			this.stamptext(iLayer+"-"+i, (int)bd.x2()-200, grid.nY-70-yMinus, (int)bd.y1()+36, grid, 5);
			
			// vertical stick
			float dHole=0.032f;
			float dBord=0.030f;
			float xF1=xSegs[5]-0.04f-dBord-dHole;
			float xF2=xSegs[5];
			float zF1=zSegs[2]-0.02f-dBord-dHole;
			float zF2=zSegs[2];
			float y1f=by1;
			float y2f=by2;
			
			float xH1=xF1+dBord;
			float xH2=xH1+dHole;
			float zH1=zF1+dBord;
			float zH2=zH1+dHole;
			this.addBox(xF1, y1f, zF1, xF2,y2f, zF2, grid, depth, by1,bd);
			this.subBoxPriority(xH1, y1f, zH1,xH2, y2f, zH2, grid, depth, by1,bd);
			this.subBoxPriority(xH1-dBord, y2f-0.02f, zH1-dBord, xH2+0.02f,y2f, zH2+0.02f, grid, depth, by1,bd);
			this.subBoxPriority(xH1-dBord, y1f, zH1-dBord, xH2+0.02f,y1f+0.02f, zH2+0.02f, grid, depth, by1,bd);
			zF1=zSegs[4]-0.02f-dBord-dHole;
			zF2=zSegs[4];
			xH1=xF1+dBord;
			xH2=xH1+dHole;
			zH1=zF1+dBord;
			zH2=zH1+dHole;
			this.addBox(xF1, y1f, zF1, xF2,y2f, zF2, grid, depth, by1,bd);
			this.subBoxPriority(xH1, y1f, zH1,xH2, y2f, zH2, grid, depth, by1,bd);
			this.subBoxPriority(xH1-dBord, y2f-0.02f, zH1-dBord, xH2+0.02f,y2f, zH2+0.02f, grid, depth, by1,bd);
			this.subBoxPriority(xH1-dBord, y1f, zH1-dBord, xH2+0.02f,y1f+0.02f, zH2+0.02f, grid, depth, by1,bd);
			
			//propfen
			float dPropf=0.09f;
			y1f=by2-0.06f;
			y2f=by2;
			float y3f=by1;
			float y4f=by1+0.06f;
			for (int x=0;x<xSegs.length;x++){
				for (int z=0;z<zSegs.length;z++){
					 xF1=xSegs[x]-dPropf;
					 zF1=zSegs[z];
					 this.addBox(xF1,y1f, zF1, xF1+dPropf, y2f, zF1+dPropf, grid, depth, by1,bd);
					 if (iLayer!=0){
						 this.addBox(xF1,y3f, zF1, xF1+dPropf, y4f, zF1+dPropf, grid, depth, by1,bd);
					 }
				}
			}
			
			float cY1=by1+0.15f;
			float cY2=by1+0.19f;// 40mm higher
			float off=dHole*0.5f;
			
			
			// holes
			for (int ii = 0; ii < xSegs.length -1; ii++) {
				if ((iLayer==2||iLayer==3)&&i==1&&ii==2){
					
				}
				else{
					float mid=(xSegs[ii]+xSegs[ii+1])*0.5f;
					this.subBox(mid-off, cY1-off, sZ, mid+off, cY1+off, eZ, grid, depth, by1,bd);
				}
				if ((iLayer==3||iLayer==4||iLayer==5)&&i==3){
					float mid=(xSegs[4]+xSegs[5])*0.5f;
					this.subBoxPriority(mid-off, cY1-off, zSegs[4]-0.04f, mid+off, cY1+off,zSegs[4]+0.04f, grid, depth, by1,bd);
				}
			}
			
			for (int ii = 0; ii < zSegs.length -1; ii++) {
				float mid=(zSegs[ii]+zSegs[ii+1])*0.5f;
				this.subBox(sX, cY2-off, mid-off, eX, cY2+off, mid+off, grid, depth, by1,bd);
			}
			
			
			
			// splitLines
			for (int ii=0;ii<splitLines.size();ii++){
				Line3D  line=splitLines.get(ii);
				
				int lx1=(int)line.x1;
				int lx2=(int)line.x2;
				int ly1=Math.max((int)line.y1-y1,0);
				int ly2=Math.min((int)line.y2-y1,grid.nY);
				int lz1=(int)line.z1;
				int lz2=(int)line.z2;
				int dX=lx2-lx1;
				if (dX!=0){
					dX=(dX)/abs(dX);
				}
				int dZ=lz2-lz1;
				if (dZ!=0){
					dZ=(dZ)/abs(dZ);
				}
				int sX=lx1;
				int sZ=lz1;
				do{
					for (int ly=ly1;ly<ly2;ly++){
						grid.set(sX, ly, sZ, isoOut);
					}
					sX+=dX;
					sZ+=dZ;
				}while(sX!=lx2||sZ!=lz2);
				
			}
			
			
			// vertical front ab layer 1
			for (int ii = 0; ii < this.subBoxes.size(); ii++) {
				Box3D b = subBoxes.get(ii);
				dHole = 0.032f;
				dBord = 0.030f;
				xF1 = getX((int) b.x1) - dBord;
				xF2 = getX((int) b.x2) + dBord;
				zF1 = getZ((int) (b.z1)) - dBord;
				zF2 = getZ((int) (b.z2)) + dBord;
				y1f = getY((int) (b.y1));
				y2f = getY((int) (b.y2));
				xH1 = xF1 + dBord;
				xH2 = xH1 + dHole;
				zH1 = zF1 + dBord;
				zH2 = zH1 + dHole;
				this.addBox(xF1, y1f, zF1, xF2, y2f, zF2, grid, depth, by1, bd);
				this.subBoxPriority(xH1, y1f, zH1, xH2, y2f, zH2, grid, depth,
						by1, bd);
			}

			// vertical front ab layer 1
			for (int ii = 0; ii < this.subSolidBoxes.size(); ii++) {
				Box3D b = subSolidBoxes.get(ii);
				xH1 = getX((int) b.x1);
				xH2 = getX((int) b.x2);
				zH1 = getZ((int) b.z1);
				zH2 = getZ((int) b.z2);
				y1f = getY((int) (b.y1));
				y2f = getY((int) (b.y2));
				this.subBoxPriority(xH1, y1f, zH1, xH2, y2f, zH2, grid, depth,
						by1, bd);
			}
			// holes in layer 1 part 0
			if (iLayer==1){
				// holes
				float border=0.04f;
				float mid=zSegs[4]+border+0.021f;
				float x1=xSegs[4]-0.08f;
				float x2=xSegs[6]-0.04f;
				float cy=by1+0.07f;
		
				this.addBox(x1+0.04f, cy-off-border, mid-off-border, x2, cy+off+border, mid+off+border, grid, depth, by1,bd);
				this.subBoxPriority(x1, cy-off, mid-off, x2, cy+off, mid+off, grid, depth, by1,bd);

			}
		
			// top and down
			if (yMinus > 0) {
				//Box3D box = new Box3D(1070, 1659, 1500, 1135, 1680, 1591);//layer 3
				Box3D box = new Box3D(1160, 2062, 1360, 1400, 2086, 1600);//layer 4 
				boolean[] blobCheck;
				for (int x = 0; x < grid.nX; x++) {
					for (int z = 0; z < grid.nZ; z++) {
						grid.set(x, 0, z, isoOut);
					}
				}
				for (int y = grid.nY - 1; y >= grid.nY - yMinus-1; y--) {
					int cY = y + y1;
					Arrays.fill(solids, false);
					for (int x = 0; x < grid.nX; x++) {
						for (int z = 0; z < grid.nZ; z++) {
							if (grid.get(x, y, z)<=iso){
								solids[getIndex(x,z)]=true;
							}
						}
					}
				
					blobInfo = blobs();
					blobCheck = new boolean[blobInfo.length];
					for (int ii = 0; ii < blobInfo.length; ii++) {
						int[] blob = blobInfo[ii];
						if ((blob[0] > box.x1 && blob[2] < box.x2
								&& blob[1] > box.z1 && blob[3] < box.z2)) {
							blobCheck[ii] = true;
						}
					}
					// find the blob inside
					for (int x = 0; x < grid.nX; x++) {
						for (int z = 0; z < grid.nZ; z++) {
							int index = getIndex(x, z);
							if (solids[index]) {
								int iBlob = blobs[index];

								if (!blobCheck[iBlob]) {
									grid.set(x, y, z, isoOut);
								}
							}
							// if blob is not inside box and gridvalue is inside
							// turn it to outside.
						}
					}
				}
			} else {
				for (int x = 0; x < grid.nX; x++) {
					for (int z = 0; z < grid.nZ; z++) {
						grid.set(x, 0, z, isoOut);
						grid.set(x, grid.nY - 1, z, isoOut);
					}
				}
			}
			
			// saving
			CAADMCube mCube = new CAADMCube();
			String name = path + "part" + i+timeStamp()+".obj";
			// mirroring
			if (i==0){
				int x1=(int)bd.x1()-1;
				int x2=(int)bd.x2()+1;
				int z1=(int)bd.y1()-1;
				int z2=(int)bd.y2(); // one not enough
				float[][][]smallGrid= new float[x2-x1][grid.nY][(z2-z1)*2];
				for (int x=x1;x<x2;x++){
					for (int y=0;y<grid.nY;y++){
						for (int z=z1;z<=z2;z++){
							smallGrid[x-x1][y][z-z1]=grid.get(x, y, z);
						}
					}
				}
				
				for (int x=0;x<smallGrid.length;x++){
					for (int y=0;y<smallGrid[0].length;y++){
						for (int z=0;z<=(z2-z1);z++){
							smallGrid[x][y][smallGrid[0][0].length-z-1]=smallGrid[x][y][z];
						}
					}
				}
				mCube.marchingCubesMinim(smallGrid, iso, dim * step * 1000f, (getX(x1)-moveX) * 1000f,
						(getY(y1)-moveY) * 1000f, (getZ(z1)-mirrorZ) * 1000f, name);
			}
			else{
				mCube.marchingCubesMinim(grid.nX,grid.nY,grid.nZ,grid.values, iso, dim * step * 1000f,  (sX-moveX) * 1000f,
						(getY(y1)-moveY) * 1000f, (sZ-mirrorZ) * 1000f, name);
			}
			println("MC Grid created");
		}
	}
	public String timeStamp(){
		return "date"+month()+"_"+day()+"_"+hour();
	}
	public void selectClosestBox(){
		Box3D closestBox=null;
		float closestD=Float.MAX_VALUE;
		for (int i=0;i<fillBoxes.size();i++){
			Box3D b=fillBoxes.get(i);
			
			if (b.contains(cam.getModelX(mouseX), cY, cam.getModelY(mouseY))){
				float dX=cam.getModelX(mouseX)-b.getCenterX();
				float dY=cam.getModelY(mouseY)-b.getCenterZ();
				float d=dX*dX+dY*dY;
				if (d<closestD){
					closestBox=b;
					closestD=d;
				}
			}
		}
		if (closestBox!=null){
			setSelectedBox(closestBox);
		}
		else {
			currentBox=null;
		}
	}
	public int getRoundDown(float value,float dim){
		return (int)(value/dim);
	}
	public int getRoundUp(float value,float dim){
		return (int)(value/dim)+1;
	}
	public int getXInt(int i){
		return  (int) (i * 1f % nX);
	}

	public int getYInt(int i){
		return  (int) (i * 1f / nX);
	}

	int getIndex(int x,int y){
		return y*nX+x;
	}
	public int xK(float x) {
		return round ((x - sX) / dim);
	}

	public int yK(float y) {
		return round ((y - sY) / dim);
	}

	public int zK(float z) {
		return round ((z - sZ) / dim);
	}
	public float getX(int x) {
		return x * dim + sX;
	}

	
	public float getY(int y) {
		return y * dim + sY;
	}

	public float getZ(int z) {
		return z * dim + sZ;
	}
	public void mousePressed(){
		int cX=(int)cam.getModelX(mouseX);
		int cY=(int)cam.getModelY(mouseY);
		if (cX>0&&cX<nX&&cY>0&&cY<nZ){
			//fill(cX,cY);
			//offsetthinLines(offsetBox,20,cX,cY);
		}
		if (mouseX>300&&mouseX<width-300){
		selectClosestBox();
		}
		println("filled");
	}
	
	public void keyPressed(){
		if (key=='s')flagSaveBoxes=true;
	}
	
	class PolyBoundary extends Bounda{
		Polygon poly=new Polygon();
	}
	class PixelBoundaryDouble extends PixelBoundary{
		PixelBoundary pb2;
		int swicthY;
		PixelBoundaryDouble(int nX, int nY) {
			super(nX, nY);
			// TODO Auto-generated constructor stub
		}
		public int isBorder(int x,int y,int z){
			if (y<swicthY)
			return isBorder( x, z);
			return pb2.isBorder( x, z);
		}
	}
	class PixelBoundary extends IntGrid2D implements Boundary{
		
		int x1=0;
		int y1=0;
		int x2=nX;
		int y2=nY;
		int h1;
		int h2;
		PixelBoundary(int nX,int nY){
			
			super(nX,nY);
			x1=0;
			y1=0;
			x2=nX;
			y2=nY;
		}
		public void fillGrid(int x1,int y1,int x2,int y2,int value){
			for (int x=x1;x<x2;x++){
				for (int y=y1;y<y2;y++){
					set(x, y, value);
				}
			}
		}
		public void doBorders(){
			for (int x=0;x<nX;x++){
				for (int y=0;y<nY;y++){
					if (get(x,y)==INSIDE){
						if (x<nX-1&&get(x+1,y)==OUTSIDE){
							set(x,y,BORDER);
						}
						if (x>0&&get(x-1,y)==OUTSIDE){
							set(x,y,BORDER);
						}
						if (y>0&&get(x,y-1)==OUTSIDE){
							set(x,y,BORDER);
						}
						if (y<nY-1&&get(x,y+1)==OUTSIDE){
							set(x,y,BORDER);
						}
					}
				}
			}
			x1=nX;
			y1=nY;
			x2=0;
			y2=0;
			for (int x=0;x<nX;x++){
				for (int y=0;y<nY;y++){
					if (get(x,y)!=OUTSIDE){
						x1=min(x,x1);
						y1=min(y,y1);
						x2=max(x,x2);
						y2=max(y,y2);
					}
				}
			}
			x2++;
			y2++;
		}
		public int isBorder(int x,int y,int z){
			return isBorder( x, z);
		}
		public int isBorder(int x,int y){
			// inside stronger border
			return this.get(x, y);
		}
		public float x1(){return x1;}
		public float y1(){return y1;}
		public float x2(){return x2;}
		public float y2(){return y2;}
	}
	
	interface  Boundary{
		final static int INSIDE=0;
		final static int BORDER=1;
		final static int OUTSIDE=2;
		public int isBorder(int x,int y,int z);
		public float x1();
		public float y1();
		public float x2();
		public float y2();
	}
	class Bounda extends Rect2D implements Boundary{
		
		public Bounda(){
			
		}
		public Bounda(float x1, float y1, float x2, float y2) {
			super(x1, y1, x2, y2);
			// TODO Auto-generated constructor stub
		}
		public int isBorder(int x,int y,int z){
			return isBorder(x,z);
		}
		public int isBorder(int x,int y){
			if (x>=x1&&x<x2&&y>=y1&&y<y2){
				if (x==x1||x==x2-1||y==y1||y==y2-1)return BORDER;
				return INSIDE;
			}
			return OUTSIDE;
		}
		public float x1(){
			return x1;
		}
		public float y1(){
			return y1;
		}
		public float x2(){
			return x2;
		}
		public float y2(){
			return y2;
		}
	}
	class MenuListener implements ActionListener { 
		MenuListener() {
		  }

		@Override
		public void actionPerformed(ActionEvent e) {
			// TODO Auto-generated method stub
			if (((MenuItem)e.getSource()).getLabel().equals("New Box")){
				flagAddBox=true;
			}
			if (((MenuItem)e.getSource()).getLabel().equals("Delete Box")){
				flagDeleteBox=true;
			}
			if (((MenuItem)e.getSource()).getLabel().equals("Display Boxes")){
				displayBoxes=!displayBoxes;
			}
			if (((MenuItem)e.getSource()).getLabel().equals("Display Grid")){
				displayGrid=!displayGrid;
			}
		} 
	}
}
